/*
* Copyright 2009 NOR_/D Labs <http://labs.nortd.com>
*
* This file is part of SceneExpression.
* SceneExpression is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License 
* as published by the Free Software Foundation, either version 3 of 
* the License, or (at your option) any later version. For details
* see <http://www.gnu.org/licenses/>.
*
* * *
* 
*/


#include <sx/Shape.h>

using namespace sx;


Shape::Shape() {
        
    _geometry = new osg::Geometry();
    //geometry->setUserData(this);
                
    _vertices = new osg::Vec3Array;       
    _geometry->setVertexArray(_vertices.get());
    
    _colors = new osg::Vec4Array;
    setColor(0.7f, 0.7f, 0.0f, 1.0f);
    _geometry->setColorArray(_colors.get());
    _geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
    
    _normals = new osg::Vec3Array;
    _normals->push_back(osg::Vec3(0.0f,0.0f,1.0f));
    _geometry->setNormalArray(_normals.get());
    _geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
    
    _drawArray = new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, _vertices->size());
    _geometry->addPrimitiveSet(_drawArray.get()); 
    
    _geode->addDrawable( _geometry.get() );
    
    bPerVertexColorBindingActive = false;
    bPerVertexNormalBindingActive = false;
}

void Shape::setMode( GLenum mode ) {
    // mode is one of the following
    // osg::PrimitiveSet::POINTS  or  GL_POINTS
    // osg::PrimitiveSet::LINES  or  GL_LINES
    // osg::PrimitiveSet::LINE_STRIP  or  GL_LINE_STRIP
    // osg::PrimitiveSet::LINE_LOOP  or  GL_LINE_LOOP
    // osg::PrimitiveSet::TRIANGLES  or  GL_TRIANGLES
    // osg::PrimitiveSet::TRIANGLE_STRIP  or  GL_TRIANGLE_STRIP
    // osg::PrimitiveSet::TRIANGLE_FAN  or  GL_TRIANGLE_FAN
    // osg::PrimitiveSet::QUADS  or  GL_QUADS, 
    // osg::PrimitiveSet::QUAD_STRIP  or  GL_QUAD_STRIP
    // osg::PrimitiveSet::POLYGON  or  GL_POLYGON 
    _drawArray->set(mode, _drawArray->getFirst(), _drawArray->getCount());
}

void Shape::addVertex( float x, float y, float z ) {
    addVertex(osg::Vec3(x, y, z));
}
void Shape::addVertex(osg::Vec3 vec) {
    _vertices->push_back(vec);
    _drawArray->setCount(_vertices->size());
    _geometry->dirtyDisplayList();
}
void Shape::clearVertices() {
    _vertices->clear();
}

void Shape::addVertexColor( float r, float g, float b, float a ) {
    addVertexColor(osg::Vec4(r,g,b,a));
}
void Shape::addVertexColor( osg::Vec4 colorvec ) {
    // Once you start adding colors you are switiching to per-vertex mode.
    // This also means you need to have exactly one color per vertex.
    if( !bPerVertexColorBindingActive) {
        // The first time this is called clear all colors.
        _colors->clear();
        bPerVertexColorBindingActive = true;
    }
    _colors->push_back(colorvec);
    _geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
    _geometry->dirtyDisplayList();
}

void Shape::setColor( float r, float g, float b, float a ) {
    setColor(osg::Vec4(r,g,b,a));
}
void Shape::setColor( osg::Vec4 color ) {
    // This bind one color to all verteces.
    _colors->clear();
    _colors->push_back(color);
    _geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
}

void Shape::addVertexNormal( float x, float y, float z ) {
    addVertexNormal(osg::Vec3(x,y,z));
}
void Shape::addVertexNormal( osg::Vec3 normal ) {
    // Once you start adding normals you are switiching to per-vertex mode.
    // This also means you need to have exactly one normal per vertex.
    if( !bPerVertexNormalBindingActive) {
        // The first time this is called clear all normals.
        _normals->clear();
        bPerVertexNormalBindingActive = true;
    }
    _normals->push_back(normal);
    _geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
    _geometry->dirtyDisplayList();    
}

void Shape::setNormal( float x, float y, float z ) {
    setNormal(osg::Vec3(x,y,z));
}
void Shape::setNormal( osg::Vec3 normal ) {
    // This bind one color to all verteces.
    _normals->clear();
    _normals->push_back(normal);
    _geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
}
    
void Shape::tesselate() {
    if( _tesselator.get() == NULL ) {
        _tesselator = new osgUtil::Tessellator();
        _tesselator->setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD);
    }
    _tesselator->retessellatePolygons(*(_geometry.get()));
}







// Primitive Shapes
//
//

    
Shape2D::Shape2D() {
    _bFill = true;
    setMode(osg::PrimitiveSet::POLYGON);
}

void Shape2D::setPosition( float x, float y, float z) {
    _xform->setPosition(osg::Vec3(x, y, z));
}

void Shape2D::setFill( bool bFill ) {
    _bFill = bFill;
    if( _bFill ) {
        setMode(osg::PrimitiveSet::POLYGON);
    } else {
        setMode(osg::PrimitiveSet::LINE_LOOP);
    }
}







ImageShape::ImageShape() {
    setMode(osg::PrimitiveSet::QUADS);
    disableLighting();
    
    _texcoords = new osg::Vec2Array();
    _geometry->setTexCoordArray(0, _texcoords);
}

float ImageShape::getWidth() {
    if(_image.valid()) {
        return _image->s();
    } else {
        osg::notify(osg::WARN) << "in Image::getWidth(), no image loaded" << std::endl;
        return 0.0f;
    }
}

float ImageShape::getHeight() {
    if(_image.valid()) {
        return _image->t();
    } else {
        osg::notify(osg::WARN) << "in Image::getHeight(), no image loaded" << std::endl;
        return 0.0f;
    }
}
    
void ImageShape::loadImage( std::string filename ) {
    _image = osgDB::readImageFile(filename);
    if(filename.substr(filename.length()-3) == "bmp") {
        _image->flipVertical();
    }
    
    addVertex(0.f, _image->t(), 0.f);
    addVertex(0.f, 0.f, 0.f);
    addVertex(_image->s(), 0.f, 0.f);
    addVertex(_image->s(), _image->t(), 0.f);
    
    addTextureCoordinate(0.0f,1.0f);
    addTextureCoordinate(0.0f,0.0f);
    addTextureCoordinate(1.0f,0.0f);
    addTextureCoordinate(1.0f,1.0f);

    setNormal(1.0f, 0.0f, 0.0f);
    setColor(1.0f, 1.0f, 1.0f, 1.0f);

    // set up the texture state.
    _texture = new osg::Texture2D;
    _texture->setDataVariance(osg::Object::DYNAMIC);  // protect from being optimized away as static state.
    _texture->setImage(_image);

    osg::StateSet* stateset = _geometry->getOrCreateStateSet();
    stateset->setTextureAttributeAndModes(0, _texture, osg::StateAttribute::ON);

    _texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR_MIPMAP_LINEAR);
    _texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
}

void ImageShape::addTextureCoordinate( float u, float v ) {
    addTextureCoordinate(osg::Vec2(u,v));
}
void ImageShape::addTextureCoordinate( osg::Vec2 texcoord ) {
    _texcoords->push_back(texcoord);
    _geometry->dirtyDisplayList();    
}
        





TriangleShape::TriangleShape( float a1, float a2, float b1, float b2, float c1, float c2 ) {
    addVertex(a1, a2, 0.f);
    addVertex(b1, b2, 0.f);
    addVertex(c1, c2, 0.f);
}

void TriangleShape::setVertices( float a1, float a2, float b1, float b2, float c1, float c2 ) {
    _vertices->clear();
    addVertex(a1, a2, 0.f);
    addVertex(b1, b2, 0.f);
    addVertex(c1, c2, 0.f);
    _geometry->dirtyDisplayList();
}





RectangleShape::RectangleShape( float width, float height ) {
    _width = width;
    _height = height;
    addVertex(0.f, 0.f, 0.f);
    addVertex(_width, 0.f, 0.f);
    addVertex(_width, _height, 0.f);
    addVertex(0.f, _height, 0.f);
}

void RectangleShape::setWidth( float width ) {
    _width = width;
    _vertices.get()->at(1)[0] = _width;
    _vertices.get()->at(2)[0] = _width;
    _geometry->dirtyDisplayList();
}

void RectangleShape::setHeight( float height ) {
    _height = height;
    _vertices.get()->at(2)[1] = _height;
    _vertices.get()->at(3)[1] = _height;
    _geometry->dirtyDisplayList();
}






EllipseShape::EllipseShape( float width, float height ) {
    _width = width;
    _height = height;
    setResolution(40);
}

void EllipseShape::setResolution( int res ) {
    if (res > 1 && res != _resolution) {
        _resolution = res;
        _vertices->clear();
        float angle = 0.0f;
        float angleAdder = 2*osg::PI / (float)_resolution;
        for (int i = 0; i < _resolution; i++){
            addVertex(cos(angle)*_width*0.5, sin(angle)*_height*0.5, 0.0f);
            angle += angleAdder;
        }
    }
}
int EllipseShape::getResolution() {
    return _resolution;
}

void EllipseShape::setWidth( float width ) {
    _width = width;
    setResolution(_resolution);
}

void EllipseShape::setHeight( float height ) {
    _height = height;
    setResolution(_resolution);
}




LineShape::LineShape() {
    setMode(osg::PrimitiveSet::LINE_STRIP);
}

void LineShape::setLineWidth( float width ) {
    _lineWidth = width;
    osg::StateSet* stateset = _geode->getOrCreateStateSet();
    osg::LineWidth* lw = new osg::LineWidth(width);
    stateset->setAttributeAndModes(lw, osg::StateAttribute::ON);
}

float LineShape::getLineWidth() {
    return _lineWidth;
}





MonsterShape::MonsterShape() {
    addVertex(5, 120, 0);     //left elbow
    addVertex(10, 80, 0);
    addVertex(15, 80, 0);
    addVertex(20, 110, 0);
    addVertex(30, 115, 0);
    addVertex(30, 40, 0);
    addVertex(50, 35, 0);
    addVertex(50, 30, 0);
    addVertex(35, 20, 0);
    addVertex(35, 15, 0);
    addVertex(55, 20, 0);
    addVertex(63, 33, 0);
    addVertex(100, 30, 0);
    addVertex(100, 25, 0);
    addVertex(80, 12, 0);
    addVertex(80, 5, 0);      //right big toe
    addVertex(110, 15, 0);
    addVertex(115, 30, 0);
    addVertex(130, 30, 0);
    addVertex(130, 100, 0);
    addVertex(145, 95, 0);
    addVertex(145, 55, 0);
    addVertex(150, 55, 0);
    addVertex(165, 115, 0);  //left elbow
    addVertex(130, 150, 0);
    addVertex(150, 170, 0);
    addVertex(130, 190, 0);
    addVertex(35, 190, 0);
    addVertex(15, 170, 0);
    addVertex(32, 150, 0);
    tesselate();
    disableDepthTest();
    disableLighting();

    _mouthpoints = new Shape();
    _mouthpoints->setMode(osg::PrimitiveSet::POLYGON);
    _mouthpoints->addVertex(5, 10, 0);
    _mouthpoints->addVertex(10, 25, 0); //
    _mouthpoints->addVertex(68, 23, 0); //
    _mouthpoints->addVertex(72, 0, 0);
    _mouthpoints->addVertex(75, 33, 0);
    _mouthpoints->addVertex(5, 33, 0);
    _mouthpoints->setPosition(40, 120, 0);
    _mouthpoints->tesselate();
    addChild(_mouthpoints);

    _leyepoints = new Shape();
    _leyepoints->setMode(osg::PrimitiveSet::POLYGON);
    _leyepoints->addVertex(40, 180, 0);
    _leyepoints->addVertex(40, 170, 0);
    _leyepoints->addVertex(50, 170, 0); 
    _leyepoints->addVertex(50, 180, 0);
    addChild(_leyepoints);

    _reyepoints = new Shape();
    _reyepoints->setMode(osg::PrimitiveSet::POLYGON);
    _reyepoints->addVertex(110, 180, 0);
    _reyepoints->addVertex(110, 170, 0);
    _reyepoints->addVertex(120, 170, 0);
    _reyepoints->addVertex(120, 180, 0);
    addChild(_reyepoints);
    
    setColor(1, 0.19, 0.14, 1);      
}

MonsterShape::~MonsterShape() {
    delete _mouthpoints;
    delete _leyepoints;
    delete _reyepoints;
}

void MonsterShape::setColor( float r, float g, float b, float a ) {
    setColor(osg::Vec4(r,g,b,a));
}
    
void MonsterShape::setColor( osg::Vec4 color ) {
    _colors->clear();
    _colors->push_back(color);
    _geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
    _mouthpoints->setColor(color/3.0f);
    _leyepoints->setColor(color/3.0f);
    _reyepoints->setColor(color/3.0f);
}



